import * as THREE from 'three';
import Event from '../Event';
import Debug from '../tools/Debug';
import ShapeEditor from '../ShapeEditor';
import CropPlot from './CropPlot';
import CropRack from './CropRack';
import GreenHouse from '../GreenHouse/GreenHouse';
import { editStrategies } from './Strategies';
import ResourceTracker from '../tools/ResourceTracker';
import { useContextMenu } from '../tools/useContextMenu';
import { CUSTOM_MENU_ID } from '../tools/constants';

interface PlantAreaManagerEvents {
  [PlantAreaManager.EVENT_CROP_STAFF_UPDATE]: (result: { cropPlots?: Array<CropPlot>; cropRacks?: Array<CropRack> }) => void;
}
export type CropStaff = CropPlot | CropRack;

export default class PlantAreaManager extends Event<PlantAreaManagerEvents> {
  /** PLOT 或 RACK 更新事件 */
  static EVENT_CROP_STAFF_UPDATE = 'PLANT_AREA_MANAGER_CROP_STAFF_UPDATE';

  private readonly namespace = 'PlantAreaManager';

  /** 用于挂载plot和rack  */
  cropStaffGroup = new THREE.Group();

  /** 种植块Map */
  private _cropPlots: Map<string | number, CropPlot> = new Map();
  cropPlots: Map<string | number, CropPlot> = new Map();

  /** 种植架 */
  private _cropRacks: Map<string | number, CropRack> = new Map();
  cropRacks: Map<string | number, CropRack> = new Map();

  editingCropStaff: CropStaff = null;

  scene: THREE.Scene = null;
  camera: THREE.PerspectiveCamera = null;
  controls: Editor.Controls = null;

  /** 建筑物或田地 */
  building: Editor.Building = null;

  shapeEditor: ShapeEditor = null;

  resourceTracker = new ResourceTracker();

  constructor(option: Editor.PlantAreaManagerOption) {
    super();
    const { scene, building, controls, camera } = option;
    this.scene = scene;
    this.building = building;
    this.controls = controls;
    this.camera = camera;
    this._initCropPlotsProxy();
    this._initCropRacksProxy();
    this.init(option);
  }

  /** 代理cropPlots */
  private _initCropPlotsProxy(): void {
    const self = this;
    this.cropPlots = new Proxy(this._cropPlots, {
      get(target, prop, receiver) {
        if (prop === 'set') {
          return function (id: number | string, cropPlot: Editor.CropPlot) {
            const result = target.set(id, cropPlot);
            self._emitEvent(PlantAreaManager.EVENT_CROP_STAFF_UPDATE, { cropPlots: Array.from(target.values()) });
            return result;
          };
        }
        if (prop === 'delete') {
          return function (id: number | string) {
            const result = target.delete(id);
            self._emitEvent(PlantAreaManager.EVENT_CROP_STAFF_UPDATE, { cropPlots: Array.from(target.values()) });
            return result;
          };
        }
        const value = Reflect.get(target, prop, receiver);
        if (typeof value === 'function') {
          return value.bind(target);
        }
        return value;
      },
    });
  }

  /** 代理cropRacks */
  private _initCropRacksProxy(): void {
    const self = this;
    this.cropRacks = new Proxy(this._cropRacks, {
      get(target, prop, receiver) {
        if (prop === 'set') {
          return function (id: number | string, cropRack: Editor.CropRack) {
            const result = target.set(id, cropRack);
            self._emitEvent(PlantAreaManager.EVENT_CROP_STAFF_UPDATE, { cropRacks: Array.from(target.values()) });
            return result;
          };
        }
        if (prop === 'delete') {
          return function (id: number | string) {
            const result = target.delete(id);
            self._emitEvent(PlantAreaManager.EVENT_CROP_STAFF_UPDATE, { cropRacks: Array.from(target.values()) });
            return result;
          };
        }
        const value = Reflect.get(target, prop, receiver);
        if (typeof value === 'function') {
          return value.bind(target);
        }
        return value;
      },
    });
  }

  /** 手动触发UPDATE事件 */
  _manualEmitUpadateEvent = () => {
    this._emitEvent(PlantAreaManager.EVENT_CROP_STAFF_UPDATE, {
      cropPlots: Array.from(this.cropPlots.values()),
      cropRacks: Array.from(this.cropRacks.values()),
    });
  };

  // 由pluginManager触发
  init(option: Editor.PlantAreaManagerOption): void {
    const { scene, building } = option;

    // 初始化group
    building.followBasePoint(this.cropStaffGroup);
    this.cropStaffGroup.name = 'PlantAreaManager-CropStaffGroup';
    this.resourceTracker.track(this.cropStaffGroup);
    scene.add(this.cropStaffGroup);

    // 预渲染plot
    const { preloadCropPlotRawDatas, preloadCropRackRawDatas } = option;
    this.renderCropPlots(preloadCropPlotRawDatas);
    this.renderCropRacks(preloadCropRackRawDatas);
  }
  /**
   * 打开形状编辑器
   */
  enableShapeEditor(): void {
    if (!this.shapeEditor) {
      const preloadRawDatas: Array<Editor.CropPlotRawData> = [];
      for (const [id, cropPlot] of this.cropPlots) {
        preloadRawDatas.push(cropPlot.getRawData());
      }
      const shapeEditorOption: Editor.ShapeEditorOption = {
        building: this.building,
        preloadRawDatas,
      };
      this.shapeEditor = new ShapeEditor(shapeEditorOption);
      this.shapeEditor.on(ShapeEditor.EVENT_CLOSE, () => {
        this.shapeEditor.destroy();
        this.shapeEditor = null;
      });
      this.shapeEditor.on(ShapeEditor.EVENT_SAVE, this.renderCropPlots.bind(this));
    }
  }

  /**
   * 形状编辑器保存后触发
   * @param renderRawDatas - 原始渲染数据
   */
  renderCropPlots(plotSaveDatas: Array<Editor.CropPlotSaveData>): void {
    if (Array.isArray(plotSaveDatas)) {
      plotSaveDatas.forEach((plotSaveData) => {
        const { data, pointArr, ...resPlotSaveData } = plotSaveData;
        const { id } = data;
        if (this.cropPlots.has(id)) {
          // 更新已有plot的shape
          const cropPlot = this.cropPlots.get(id);
          cropPlot.updateShape({ data, pointArr });
          Debug.log(this.namespace, '更新cropPlot', cropPlot);
        } else {
          // 新增plot
          const cropPlot = new CropPlot({
            rawData: plotSaveData,
            ...resPlotSaveData,
            cropStaffGroup: this.cropStaffGroup,
            building: this.building,
          });
          this.cropPlots.set(id, cropPlot);
          Debug.log(this.namespace, '新增cropPlot', cropPlot);
        }
      });
    }
    // 销毁shapeEditor
    this.shapeEditor && this.shapeEditor.destroy();
    this.shapeEditor = null;
  }

  /**
   * @description 删除单个种植块
   */
  removeCropPlotById(id: string | number): void {
    const cropPlot = this.getCropPlotById(id);
    if (cropPlot instanceof CropPlot) {
      cropPlot.destroy();
      this.cropPlots.delete(id);
    }
  }

  /**  删除所有种植块*/
  removeAllCropPlots(): void {
    this.cropPlots.forEach((cropPlot, id) => {
      this.removeCropPlotById(id);
    });
    this.cropPlots.clear();
  }

  /** 根据id获取 */
  getCropPlotById(id: string | number): CropPlot | null {
    return this.cropPlots.get(id);
  }

  /** 新建一个CropRack */
  createCropRack(option: Omit<Editor.CropRackOption, 'cropStaffGroup' | 'scene'>, isFastClone?: boolean): void {
    isFastClone = isFastClone || false;
    const cropRack = new CropRack({
      ...option,
      cropStaffGroup: this.cropStaffGroup,
      scene: this.scene,
    });
    cropRack.on(CropRack.EVENT_BASEMODEL_LOADED, () => {
      this.cropRacks.set(cropRack.id, cropRack);
      if (isFastClone) {
        /**
         * MARK: 必须强制更新，否则会出现editStrategies获取坐标滞后
         * @see editStrategies['cropRack']
         */
        cropRack.group.updateMatrixWorld(true);
        this.openEdit(cropRack);
      }
    });
  }

  renderCropRacks(rackRawDatas: Array<Editor.CropRackSaveData>): void {
    if (Array.isArray(rackRawDatas)) {
      rackRawDatas.forEach((rackRawData) => {
        this.createCropRack(rackRawData);
      });
    }
  }

  removeCropRackById(id: string | number): void {
    const cropRack = this.cropRacks.get(id);
    if (cropRack instanceof CropRack) {
      /**
       * 安全删除检查，与openEdit的attach对应
       * @see openEdit
       */
      if (this.controls.isObjectControlled(cropRack.group)) {
        this.closeEdit();
      }
      cropRack.destroy();
      this.cropRacks.delete(id);
    }
  }

  removeAllCropRacks(): void {
    this.cropRacks.forEach((cropRack, id) => {
      this.removeCropRackById(id);
    });
    this.cropRacks.clear();
  }

  private _objectChangeHandler: () => void;

  /** 开启编辑 */
  openEdit(staff: CropStaff): void {
    if (!this.controls) return;
    // 如果被控对象相同，则不触发开启
    if (this.controls.isObjectControlled(staff.group)) return;
    // 静默关闭一次编辑，确保状态正确
    this.closeEdit();

    const { transformControls } = this.controls;
    if (staff instanceof CropRack) {
      const locateStrategy = editStrategies['cropRack'];
      // 定位
      locateStrategy &&
        locateStrategy({
          camera: this.camera,
          controls: this.controls,
          building: this.building,
          staff,
        });
      // controls视角跟随

      // 修正事件监听器的添加方式
      this._objectChangeHandler = () => {
        staff.controlsChangeHandler(this.controls);
      };
      transformControls.addEventListener('objectChange', this._objectChangeHandler);
      // MARK：暂时只有cropRack会触发
      transformControls.attach(staff.group);
      // 开启cropRack快速克隆
      this.enableFastCloneCropRack();
    }
    this.editingCropStaff = staff;
  }

  /** 关闭编辑 */
  closeEdit(): void {
    if (!this.editingCropStaff) return;
    const staff = this.editingCropStaff;

    // 归还屋顶透明度
    this.building instanceof GreenHouse && this.building.setProofOpacity(1);

    if (staff instanceof CropRack) {
      // 关闭轨道控制
      this.controls.detachTransfromControls();
      const { transformControls } = this.controls;
      transformControls.removeEventListener('objectChange', this._objectChangeHandler);
      // 关闭快速克隆
      this.disableFastCloneCropRack();
      this._objectChangeHandler = () => {};
      Debug.log(this.namespace, 'closeEdit', staff.getSaveData());
    }
    this.editingCropStaff = null;
  }

  _followTransfromMouseDownHandler = () => {
    this.cropStaffGroup.visible = false;
  };
  _followTransfromMouseUpHandler = () => {
    // 位置跟随修正
    this.cropStaffGroup.position.copy(this.building.model.position);
    // 旋转跟随修正
    this.cropStaffGroup.quaternion.copy(this.building.model.quaternion);
    this.cropStaffGroup.visible = true;
  };

  /** 开始cropStaffGroup跟随变换 */
  enablefollowTransform(): void {
    const controls = this.controls;
    if (!controls) {
      Debug.error(this.namespace, '-enablefollowTransform-未持有controls,请检查配置');
      return;
    }
    const { transformControls } = controls;
    transformControls.addEventListener('mouseDown', this._followTransfromMouseDownHandler);
    transformControls.addEventListener('mouseUp', this._followTransfromMouseUpHandler);
  }

  /**  关闭cropStaffGroup跟随变换 */
  disablefollowTransform(): void {
    const controls = this.controls;
    if (!controls) {
      Debug.error(this.namespace, '-disablefollowTransform-未持有controls,请检查配置');
      return;
    }
    const { transformControls } = controls;
    transformControls.removeEventListener('mouseDown', this._followTransfromMouseDownHandler);
    transformControls.removeEventListener('mouseUp', this._followTransfromMouseUpHandler);
  }

  private fastCloneCropRackAbortController: AbortController;

  /**
   * @description 快速克隆cropRack
   *  - 与disableFastCloneCropRack 成对调用
   */
  enableFastCloneCropRack() {
    const { renderer } = this.controls.option;
    const box = renderer.domElement;
    /** 避免重复监听 */
    if (this.fastCloneCropRackAbortController) return;
    this.fastCloneCropRackAbortController = new AbortController();
    box.addEventListener(
      'contextmenu',
      (mouseEvent) => {
        mouseEvent.preventDefault();
        if (this.editingCropStaff) {
          const menu = useContextMenu({
            mouseEvent,
            id: CUSTOM_MENU_ID,
            items: [
              {
                text: '快速复制',
                callback: () => {
                  const cropRack = this.editingCropStaff as CropRack;
                  if (!cropRack) {
                    document.body.removeChild(menu);
                    return;
                  }
                  const { pos, url, scale, cropModelInfo } = cropRack.getSaveData();
                  // 克隆模型的位置在该个cropRack的position的x基础上偏移50
                  pos.position = new THREE.Vector3(pos.position.x + 50, pos.position.y, pos.position.z);
                  // TODO: 如果添加cropModelInfo，将会一并复制作物模型
                  this.createCropRack({ url, scale, pos }, true);
                  // 销毁menu
                  document.body.removeChild(menu);
                },
              },
            ],
          });
        }
      },
      {
        signal: this.fastCloneCropRackAbortController.signal,
      }
    );
  }

  disableFastCloneCropRack() {
    /**
     * FIXME: 需要小心处理editingCropStaff的引用
     */
    this.fastCloneCropRackAbortController?.abort();
    this.fastCloneCropRackAbortController = null;
  }

  getSaveData(): {
    cropPlots: Array<Editor.CropPlotSaveData>;
    cropRacks: Array<Editor.CropRackSaveData>;
  } {
    return {
      cropPlots: Array.from(this.cropPlots.values()).map((cropPlot) => cropPlot.getSaveData()),
      cropRacks: Array.from(this.cropRacks.values()).map((cropRack) => cropRack.getSaveData()),
    };
  }

  /** 销毁 种植区域管理器 */
  destroy(): void {
    this.shapeEditor && this.shapeEditor.destroy();
    this.closeEdit();
    this.disablefollowTransform();
    /**
     * 分别移除所有的cropPlot和cropRack
     */
    this.removeAllCropPlots();
    this.removeAllCropRacks();
    this.scene.remove(this.cropStaffGroup);
    this._removeEvents();
  }
}
